/*****************************************************************************
 *  This file is part of the OpenDomo project.
 *  Copyright(C) 2012 OpenDomo Services SL
 *  
 *  Daniel Lerch Hostalot <dlerch@opendomo.com>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *****************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <stdint.h>
#include <locale.h>

#include <virtual_ports_widgets.h>
#include <links_widgets.h>
#include <config.h>

#define PORT 1729


GtkBuilder *builder;

char p_ai[8][16];
char p_di[8][16];
char p_do[8][16];
char p_vt[16][16];

config_t cfg;

typedef struct _Data Data;
struct _Data
{
	GtkBuilder *builder;
};


virtual_selector_widgets_t *vsw[16];
link_selector_widgets_t *lsw1[8];
link_selector_widgets_t *lsw2[8];

#define return_message(x) { message_box(x); return; }

// {{{ is_text()
int is_text(const char *str)
{
	const char *p = str;
	while(*p)
	{
		if(!isalnum(*p) && *p!='_') 
			return 0;
		p++;
	}

	return 1;
}
// }}}

// {{{ is_num()
int is_num(const char *str)
{
	if(strlen(str)==0)
		return 0;

	const char *p = str;

	if(p[0]=='-' || p[0]=='+')
		p++;

	while(*p)
	{
		if(!isdigit(*p)) 
			return 0;
		p++;
	}

	return 1;
}
// }}}

// {{{ is_real()
int is_real(const char *str)
{
	if(strlen(str)==0)
		return 0;

	if(is_num(str))
		return 1;

	const char *p = str;

	if(p[0]=='-' || p[0]=='+')
		p++;

	int has_dot=0;

	while(*p)
	{
		if(*p=='.' && !has_dot)
		{
			p++;
			has_dot=1;
			continue;
		}	

		if(!isdigit(*p)) 
			return 0;
		p++;
	}

	return 1;
}
// }}}

// {{{ is_hex()
int is_hex(const char *str)
{
	if(strlen(str)==0)
		return 0;

	const char *p = str;

	while(*p)
	{
		if(!isxdigit(*p)) 
			return 0;
		p++;
	}

	return 1;
}
// }}}


// {{{ exec_cmd()
int exec_cmd(const char *cmd, const char *host, 
	char *response, size_t response_len)
{
	printf("cmd: %s", cmd);

	GError *err = NULL;
	GSocket *sock;
	GInetAddress *iaddr = NULL;
	GSocketAddress *saddr = NULL;
	GCancellable *cancel = NULL;

	g_type_init();
	iaddr = g_inet_address_new_from_string(host);
  	saddr = g_inet_socket_address_new(iaddr, PORT);

	sock = g_socket_new(
		G_SOCKET_FAMILY_IPV4,
		G_SOCKET_TYPE_STREAM,
		G_SOCKET_PROTOCOL_TCP,
		&err);

	if(!g_socket_connect(sock, saddr, cancel, &err))
	{
		printf("exec_cmd() cannot connect to %s:%d\n", host, PORT);
		return -1;
	}

	g_socket_send(sock, cmd, strlen(cmd), cancel, &err); 
	int r = g_socket_receive(sock, response, response_len, cancel, &err); 
	if(r>=0 && r<response_len)
		response[r]=0;
	else
		response[response_len-1]=0;

	if(strstr(response, "E"))
		printf("response: %s\n", response);
	return 0;
}
// }}}

// {{{ message_box()
void message_box(const char* msg)
{
	GtkWidget *win = GTK_WIDGET(
		gtk_builder_get_object(builder, "window_main"));

	GtkWidget *dialog = gtk_message_dialog_new (
			GTK_WINDOW(win),
			GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_ERROR,
			GTK_BUTTONS_CLOSE,
			msg, NULL);
	gtk_dialog_run (GTK_DIALOG (dialog));
	gtk_widget_destroy (dialog);

}
// }}}


// {{{ get_entry_value()
const char *get_entry_value(const char *name)
{
	GtkWidget *entry = GTK_WIDGET(gtk_builder_get_object(builder, name));
	const char *text = gtk_entry_get_text(GTK_ENTRY(entry));

	return text;
}
// }}}

// {{{ get_checkbutton_value()
int get_checkbutton_value(const char *name)
{
	GtkWidget *checkbutton = GTK_WIDGET(gtk_builder_get_object(builder, name));

	return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton));
}
// }}}

// {{{ get_adjustment_value()
int get_adjustment_value(const char *name)
{
	GtkAdjustment *adjustment = 
		GTK_ADJUSTMENT(gtk_builder_get_object(builder, name));

	return gtk_adjustment_get_value(GTK_ADJUSTMENT(adjustment));
}
// }}}


// {{{ cb_quit()
G_MODULE_EXPORT void cb_quit( GtkButton *button, Data *data )
{
	gtk_main_quit();
}
// }}}

// {{{ cb_update()
G_MODULE_EXPORT void cb_update( GtkButton *button, Data *data )
{
	int i;
	GtkWidget * widget = NULL;
	char cmd[32];
	char buffer[1024];

	const char connerr[] = 
	"No se ha podido conectar con ODControl, revise la dirección de conexión.";


	GtkWidget *entry_odcontrol_addr = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_odcontrol_addr"));

	const char *host = gtk_entry_get_text(GTK_ENTRY(entry_odcontrol_addr));
	if(strlen(host)==0)
	{
		message_box("Debe especificar la dirección de ODControl.");
		return;
	}

	int i_ai=0;
	int i_di=0;
	int i_do=0;
	int i_vt=0;


	// {{{ port list
	if(exec_cmd("lst\n", host, buffer, sizeof(buffer))<0)
	{
		message_box(connerr);
		return;
	}

	char *p_e = NULL;
	char *p_s = buffer;
	while((p_e=strstr(p_s, "\n")))
	{
		char type[16] = "";
		char port[16] = "";
		char extra[16] = "";

		if(i_di>8 || i_do>8 || i_ai>8 || i_vt>16)
			break;

		*p_e=0;
		sscanf(p_s, "%2s:%5s:%15s", type, port, extra);
	
		if(strcmp(type, "DI")==0)
			strncpy(p_di[i_di++], port, sizeof(p_di[i_di-1]));
		else if(strcmp(type, "DO")==0 || strcmp(type, "PO")==0 )
			strncpy(p_do[i_do++], port, sizeof(p_do[i_do-1]));
		else if(strcmp(type, "AI")==0 || strcmp(type, "XX")==0 )
			strncpy(p_ai[i_ai++], port, sizeof(p_ai[i_ai-1]));
		else if(type[1]=='V')
			strncpy(p_vt[i_vt++], port, sizeof(p_vt[i_vt-1]));
		
		p_s=p_e+1;
	}
	
	/*
	for(i=0; i<8; i++)
		printf("di: %s\n", p_di[i]);
	for(i=0; i<8; i++)
		printf("do: %s\n", p_do[i]);
	for(i=0; i<8; i++)
		printf("ai: %s\n", p_ai[i]);
	for(i=0; i<16; i++)
		printf("vt: %s\n", p_vt[i]);
	*/

	// }}}


	// {{{ tab: odcontrol

	GtkWidget *entry_bname = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_bname"));

	GtkWidget *entry_version = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_version"));

	GtkWidget *entry_webuser = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_webuser"));

	GtkWidget *entry_webpass = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_webpass"));

	if(exec_cmd("ver\n", host, buffer, sizeof(buffer))<0)
	{
		message_box(connerr);
		return;
	}

	buffer[5]=0;
	buffer[17]=0;
	gtk_entry_set_text(GTK_ENTRY(entry_bname), buffer);
	gtk_entry_set_text(GTK_ENTRY(entry_version), buffer+12);

	strncpy(cfg.tab_odcontrol.board_name, buffer, CONFIG_STRING);
	strncpy(cfg.tab_odcontrol.version, buffer+12, CONFIG_STRING);


	snprintf(cmd, sizeof(cmd), "sec show\n");
	if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
	{
		message_box(connerr);
		return;
	}
	
	char *p = NULL;
	if((p=strstr(buffer+6, ":")))
	{
		*p=0;
		gtk_entry_set_text(GTK_ENTRY(entry_webuser), buffer+6);

		char *p2=NULL;
		if((p2=strstr(p+1, "\n")))
			*p2=0;
		
		gtk_entry_set_text(GTK_ENTRY(entry_webpass), p+1);
	
		strncpy(cfg.tab_odcontrol.web_user, buffer+6, CONFIG_STRING);
		strncpy(cfg.tab_odcontrol.web_pass, p+1, CONFIG_STRING);
	}
	// }}}	

	// {{{ tab: Puertos Analogicos
	for(i=0; i<8; i++)
	{
		snprintf(cmd, sizeof(cmd), "lst %s\n", p_ai[i]);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
		{
			message_box(connerr);
			return;
		}

		char port[16] = "";
		char dir[16] = "";
		char type[16] = "";
		char visib[16] = "";
		char tol[16] = "";
		char A[16] = "";
		char B[16] = "";
		char enabled[16] = "";
		sscanf(buffer, "%15s %15s %15s %15s %15s %15s %15s %15s\n", 
			port, dir, type, visib, tol, A, B, enabled);

		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), port+5);
		strncpy(cfg.tab_analogic[i].port, port+5, CONFIG_STRING);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_ai00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(strstr(visib, "hidden"))
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
			cfg.tab_analogic[i].visible = 0;
		}
		else
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);
			cfg.tab_analogic[i].visible = 1;
		}

		// Actualiza el parámetro A
		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d_A", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), A+2);
		strncpy(cfg.tab_analogic[i].A_param, A+2, CONFIG_STRING);

		// Actualiza el parámetro B
		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d_B", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), B+2);
		strncpy(cfg.tab_analogic[i].B_param, B+2, CONFIG_STRING);

		// Actualiza la tolerancia
		snprintf(widget_name, sizeof(widget_name), "adjustment_ai00%d", i);
		GtkAdjustment *adjustment 
			= GTK_ADJUSTMENT(gtk_builder_get_object(builder, widget_name));
		gtk_adjustment_set_value(adjustment, atoi(tol+4));
		cfg.tab_analogic[i].tolerance = atoi(tol+4);

		// Actualiza el campo de sensor
		snprintf(widget_name, sizeof(widget_name),"checkbutton_ai00%d_enabled",i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(strstr(enabled, "yes"))
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);
			cfg.tab_analogic[i].active = 1;
		}
		else
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
			cfg.tab_analogic[i].active = 0;
		}
	}
	// }}}

	// {{{ tab: Puertos Digitales de entrada
	for(i=0; i<8; i++)
	{
		snprintf(cmd, sizeof(cmd), "lst %s\n", p_di[i]);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
		{
			message_box(connerr);
			return;
		}

		char port[16];
		char dir[16];
		char type[16];
		char visib[16];
		sscanf(buffer, "%16s %16s %16s %16s\n", port, dir, type, visib);
			
		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_di00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), port+5);
		strncpy(cfg.tab_digital_i[i].port, port+5, CONFIG_STRING);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_di00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(strstr(visib, "hidden"))
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
			cfg.tab_digital_i[i].visible = 0;
		}
		else
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);
			cfg.tab_digital_i[i].visible = 1;
		}

	}
	// }}}

	// {{{ tab: Puertos Digitales de salida
	for(i=0; i<8; i++)
	{
		snprintf(cmd, sizeof(cmd), "lst %s\n", p_do[i]);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
		{
			message_box(connerr);
			return;
		}

		char port[16];
		char dir[16];
		char type[16];
		char visib[16];
		sscanf(buffer, "%16s %16s %16s %16s\n", 
			port, dir, type, visib);
			
		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_do00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), port+5);
		strncpy(cfg.tab_digital_o[i].port, port+5, CONFIG_STRING);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_do00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(strstr(visib, "hidden"))
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
			cfg.tab_digital_o[i].visible = 0;
		}
		else
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);
			cfg.tab_digital_o[i].visible = 1;
		}
	}
	// }}}

	// {{{ tab: Puertos Virtuales
	for(i=0; i<16; i++)
	{
		snprintf(cmd, sizeof(cmd), "lst %s\n", p_vt[i]);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
		{
			message_box(connerr);
			return;
		}

		char port[16] = "";
		char dir[16] = "";
		char type[16] = "";
		char visib[16] = "";
		char extra[64] = "";
		sscanf(buffer, "%15s %15s %15s %15s %63[&|_.+\\-,:=<>!0-9a-zA-Z ]s\n", 
			port, dir, type, visib, extra);
			
		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_vt0%.2d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), port+5);
		strncpy(cfg.tab_virtual[i].port, port+5, CONFIG_STRING);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_vt0%.2d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(strstr(visib, "hidden"))
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
			cfg.tab_virtual[i].visible = 0;
		}
		else
		{
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);
			cfg.tab_virtual[i].visible = 1;
		}


		// value
		if(strncmp(extra, "value", 5)==0)
		{	
			virtual_port_menu_select(vsw[i], "Valor");
			strncpy(cfg.tab_virtual[i].type, "Valor", CONFIG_STRING);


			if(strcmp(type+5, "digital")==0)
			{
				virtual_port_value_ad_select(vsw[i], "Digital");
				strncpy(cfg.tab_virtual[i].ad, "Digital", CONFIG_STRING);

				int a=0, b=0;
				sscanf(extra+6, "%d.%d", &a, &b);

				if(a || b)
				{
					gtk_entry_set_text(GTK_ENTRY(vsw[i]->entry_v_value), "ON");
					strncpy(cfg.tab_virtual[i].value, "ON", CONFIG_STRING);
				}
				else
				{
					gtk_entry_set_text(GTK_ENTRY(vsw[i]->entry_v_value), "OFF");
					strncpy(cfg.tab_virtual[i].value, "OFF", CONFIG_STRING);
				}
			}
			else
			{
				virtual_port_value_ad_select(vsw[i], "Analógico");
				strncpy(cfg.tab_virtual[i].ad, "Analógico", CONFIG_STRING);

				gtk_entry_set_text(GTK_ENTRY(vsw[i]->entry_v_value), extra+6);
				strncpy(cfg.tab_virtual[i].value, extra+6, CONFIG_STRING);
			}
		}

		// group
		else if(strncmp(extra, "group", 5)==0)
		{
			virtual_port_menu_select(vsw[i], "Operación");
			strncpy(cfg.tab_virtual[i].type, "Operación", CONFIG_STRING);

			char op[2];
			op[0] = extra[11];
			op[1] = 0;

			extra[11]=0;
			virtual_port_group_vp1_select(vsw[i], extra+6);
			virtual_port_group_vp2_select(vsw[i], extra+12);
			virtual_port_group_op_select(vsw[i], op);

			strncpy(cfg.tab_virtual[i].port1, extra+6, CONFIG_STRING);
			strncpy(cfg.tab_virtual[i].port2, extra+12, CONFIG_STRING);
			strncpy(cfg.tab_virtual[i].op, op, CONFIG_STRING);
		}
		

		// trigger
		else if(strncmp(extra, "trigger", 7)==0)
		{
			char op[3];
			op[0] = extra[13];
			op[1] = extra[14];
			op[2] = 0;
	
			char val[4];
			val[0] = extra[27];
			val[1] = extra[28];
			val[2] = extra[29];
			val[3] = 0;
			if(val[1]=='n') val[2]=0;


			virtual_port_menu_select(vsw[i], "Disparador");
			strncpy(cfg.tab_virtual[i].type, "Disparador", CONFIG_STRING);

			extra[20]=0;
			virtual_port_trigger_cndp_select(vsw[i], extra+15);

			extra[26]=0;
			virtual_port_trigger_outp_select(vsw[i], extra+21);

			virtual_port_trigger_cnd_select(vsw[i], op);

			printf("select: %s\n", val);
			virtual_port_trigger_outval_select(vsw[i], val);

			strncpy(cfg.tab_virtual[i].cnd, op, CONFIG_STRING);
			strncpy(cfg.tab_virtual[i].cndp, extra+15, CONFIG_STRING);
			strncpy(cfg.tab_virtual[i].outp, extra+21, CONFIG_STRING);
			strncpy(cfg.tab_virtual[i].outval, val, CONFIG_STRING);
		}

		else
		{
			virtual_port_menu_select(vsw[i], "--");
			strncpy(cfg.tab_virtual[i].type, "--", CONFIG_STRING);
		}

	}
	// }}}

	// {{{ tab: Enlaces
	snprintf(cmd, sizeof(cmd), "lln\n");
	if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
	{
		message_box(connerr);
		return;
	}

	for(i=0; i<16; i++)
	{
		strncpy(cfg.tab_links[i].type, "--", CONFIG_STRING);
	
		if(i<8)
		{
			link_sport_select(lsw1[i], "");
			link_dport_select(lsw1[i], "");
		}
		else
		{		
			link_sport_select(lsw2[i-8], "");
			link_dport_select(lsw2[i-8], "");
		}
	}

	i = 0;
	p_e = NULL;
	p_s = buffer;
	while((p_e=strstr(p_s, "\n")))
	{

		*p_e=0;

		char p1[6] = "";
		char p2[6] = "";
		char type[2] = "";
		sscanf(p_s, "%5s:%5s:%1s\n", p1, p2, type);

		if(strlen(p1)!=5 || strlen(p2)!=5)
			continue;	

		char *type_ptr = (char*)"";
		
		if(strcmp(type, "d")==0)
			type_ptr = (char*)"Directo";
		else if(strcmp(type, "i")==0)
			type_ptr = (char*)"Inverso";
		else if(strcmp(type, "p")==0)
			type_ptr = (char*)"Pulso";
		else if(strcmp(type, "c")==0)
			type_ptr = (char*)"Conmutado";
		else
		{
			type_ptr = (char*)"--";
		}

		strncpy(cfg.tab_links[i].src, p1, CONFIG_STRING);
		strncpy(cfg.tab_links[i].dst, p2, CONFIG_STRING);
		strncpy(cfg.tab_links[i].type, type_ptr, CONFIG_STRING);
				

		if(i<8)
		{
			link_sport_select(lsw1[i], p1);
			link_dport_select(lsw1[i], p2);
			link_type_select(lsw1[i], type_ptr);
		}
		else
		{		
			link_sport_select(lsw2[i-8], p1);
			link_dport_select(lsw2[i-8], p2);
			link_type_select(lsw2[i-8], type_ptr);
		}

		i++;
		p_s=p_e+1;
	}
	// }}}

	// {{{ tab: network

	GtkWidget *entry_net_ip_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_1"));
	GtkWidget *entry_net_ip_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_2"));
	GtkWidget *entry_net_ip_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_3"));
	GtkWidget *entry_net_ip_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_4"));


	GtkWidget *entry_net_mask_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_1"));
	GtkWidget *entry_net_mask_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_2"));
	GtkWidget *entry_net_mask_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_3"));
	GtkWidget *entry_net_mask_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_4"));


	GtkWidget *entry_net_gw_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_1"));
	GtkWidget *entry_net_gw_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_2"));
	GtkWidget *entry_net_gw_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_3"));
	GtkWidget *entry_net_gw_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_4"));


	GtkWidget *entry_net_mac_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_1"));
	GtkWidget *entry_net_mac_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_2"));
	GtkWidget *entry_net_mac_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_3"));
	GtkWidget *entry_net_mac_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_4"));
	GtkWidget *entry_net_mac_5 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_5"));
	GtkWidget *entry_net_mac_6 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_6"));


	snprintf(cmd, sizeof(cmd), "net sh\n");
	if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
	{
		message_box(connerr);
		return;
	}


	char mac1[8], mac2[8], mac3[8], mac4[8], mac5[8], mac6[8];
	char ip1[8], ip2[8], ip3[8], ip4[8];
	char nm1[8], nm2[8], nm3[8], nm4[8];
	char gw1[8], gw2[8], gw3[8], gw4[8];
	char param1[32];
	char param2[32];
	char param3[32];
	char param4[32];
	sscanf(buffer, "%31s %31s %31s %31s\n", param1, param2, param3, param4);
	sscanf(param1,"mac:%2s:%2s:%2s:%2s:%2s:%2s",mac1,mac2,mac3,mac4,mac5,mac6);
	sscanf(param2,"ip:%5s.%5s.%5s.%5s",ip1,ip2,ip3,ip4);
	sscanf(param3,"netmask:%5s.%5s.%5s.%5s",nm1,nm2,nm3,nm4);
	sscanf(param4,"gateway:%5s.%5s.%5s.%5s",gw1,gw2,gw3,gw4);



	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_1), mac1);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_2), mac2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_3), mac3);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_4), mac4);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_5), mac5);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_6), mac6);
	strncpy(cfg.tab_network.mac[0], mac1, CONFIG_STRING);
	strncpy(cfg.tab_network.mac[1], mac2, CONFIG_STRING);
	strncpy(cfg.tab_network.mac[2], mac3, CONFIG_STRING);
	strncpy(cfg.tab_network.mac[3], mac4, CONFIG_STRING);
	strncpy(cfg.tab_network.mac[4], mac5, CONFIG_STRING);
	strncpy(cfg.tab_network.mac[5], mac6, CONFIG_STRING);

	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_1), ip1+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_2), ip2+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_3), ip3+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_4), ip4+2);
	strncpy(cfg.tab_network.ip[0], ip1+2, CONFIG_STRING);
	strncpy(cfg.tab_network.ip[1], ip2+2, CONFIG_STRING);
	strncpy(cfg.tab_network.ip[2], ip3+2, CONFIG_STRING);
	strncpy(cfg.tab_network.ip[3], ip4+2, CONFIG_STRING);

	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_1), nm1+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_2), nm2+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_3), nm3+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_4), nm4+2);
	strncpy(cfg.tab_network.mask[0], nm1+2, CONFIG_STRING);
	strncpy(cfg.tab_network.mask[1], nm2+2, CONFIG_STRING);
	strncpy(cfg.tab_network.mask[2], nm3+2, CONFIG_STRING);
	strncpy(cfg.tab_network.mask[3], nm4+2, CONFIG_STRING);

	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_1), gw1+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_2), gw2+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_3), gw3+2);
	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_4), gw4+2);
	strncpy(cfg.tab_network.gw[0], gw1+2, CONFIG_STRING);
	strncpy(cfg.tab_network.gw[1], gw2+2, CONFIG_STRING);
	strncpy(cfg.tab_network.gw[2], gw3+2, CONFIG_STRING);
	strncpy(cfg.tab_network.gw[3], gw4+2, CONFIG_STRING);
	// }}}

}
// }}}

// {{{ internal_update()
void internal_update()
{
	int i;
	GtkWidget *widget = NULL;

	// {{{ tab: odcontrol
	GtkWidget *entry_bname = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_bname"));

	GtkWidget *entry_webuser = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_webuser"));

	GtkWidget *entry_webpass = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_webpass"));

	gtk_entry_set_text(GTK_ENTRY(entry_bname), cfg.tab_odcontrol.board_name);
	gtk_entry_set_text(GTK_ENTRY(entry_webuser), cfg.tab_odcontrol.web_user);
	gtk_entry_set_text(GTK_ENTRY(entry_webpass), cfg.tab_odcontrol.web_pass);

	// }}}	

	// {{{ tab: Puertos Analogicos
	for(i=0; i<8; i++)
	{
		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), cfg.tab_analogic[i].port);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_ai00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(cfg.tab_analogic[i].visible == 0)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
		else
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);

		// Actualiza el parámetro A
		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d_A", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), cfg.tab_analogic[i].A_param);

		// Actualiza el parámetro B
		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d_B", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), cfg.tab_analogic[i].B_param);

		// Actualiza la tolerancia
		snprintf(widget_name, sizeof(widget_name), "adjustment_ai00%d", i);
		GtkAdjustment *adjustment 
			= GTK_ADJUSTMENT(gtk_builder_get_object(builder, widget_name));
		gtk_adjustment_set_value(adjustment, cfg.tab_analogic[i].tolerance);

		// Actualiza el campo de sensor
		snprintf(widget_name, sizeof(widget_name),"checkbutton_ai00%d_enabled",i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(cfg.tab_analogic[i].active)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);
		else
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
	}
	// }}}

	// {{{ tab: Puertos Digitales de entrada
	for(i=0; i<8; i++)
	{
		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_di00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), cfg.tab_digital_i[i].port);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_di00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(cfg.tab_digital_i[i].visible == 0)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
		else
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);

	}
	// }}}

	// {{{ tab: Puertos Digitales de salida
	for(i=0; i<8; i++)
	{
		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_do00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), cfg.tab_digital_o[i].port);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_do00%d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(cfg.tab_digital_o[i].visible == 0)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
		else
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);
	}
	// }}}

	// {{{ tab: Puertos Virtuales
	for(i=0; i<16; i++)
	{
		// Actualiza el nombre del puerto
		char widget_name[32];
		snprintf(widget_name, sizeof(widget_name), "entry_vt0%.2d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		gtk_entry_set_text(GTK_ENTRY(widget), cfg.tab_virtual[i].port);

		// Actualiza el campo de visibilidad
		snprintf(widget_name, sizeof(widget_name), "checkbutton_vt0%.2d", i);
		widget = GTK_WIDGET(gtk_builder_get_object(builder, widget_name));
		if(cfg.tab_virtual[i].visible == 0)
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 0);
		else
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), 1);


		// value
		if(strcmp(cfg.tab_virtual[i].type, "Valor")==0)
		{	
			virtual_port_value_ad_select(vsw[i], cfg.tab_virtual[i].ad);
			gtk_entry_set_text(GTK_ENTRY(vsw[i]->entry_v_value), 
				cfg.tab_virtual[i].value);

		}

		// group
		if(strcmp(cfg.tab_virtual[i].type, "Operación")==0)
		{
			virtual_port_group_vp1_select(vsw[i], cfg.tab_virtual[i].port1);
			virtual_port_group_vp2_select(vsw[i], cfg.tab_virtual[i].port2);
			virtual_port_group_op_select(vsw[i], cfg.tab_virtual[i].op);
		}
		
		// trigger
		if(strcmp(cfg.tab_virtual[i].type, "Disparador")==0)
		{

			virtual_port_trigger_cndp_select(vsw[i], cfg.tab_virtual[i].cndp);
			virtual_port_trigger_outp_select(vsw[i], cfg.tab_virtual[i].outp);
			virtual_port_trigger_outval_select(vsw[i], cfg.tab_virtual[i].outval);
			virtual_port_trigger_cnd_select(vsw[i], cfg.tab_virtual[i].cnd);
		}

		else
		{
			virtual_port_menu_select(vsw[i], "--");
		}

	}
	// }}}

	// {{{ tab: Enlaces
	for(i=0; i<16; i++)
	{
		strncpy(cfg.tab_links[i].type, "--", CONFIG_STRING);
	
		if(i<8)
		{
			link_sport_select(lsw1[i], cfg.tab_links[i].src);
			link_sport_select(lsw1[i], cfg.tab_links[i].dst);
			link_type_select(lsw1[i], cfg.tab_links[i].type);
		}
		else
		{		
			link_sport_select(lsw2[i-8], cfg.tab_links[i].src);
			link_sport_select(lsw2[i-8], cfg.tab_links[i].dst);
			link_type_select(lsw2[i-8], cfg.tab_links[i].type);
		}
	}

	// }}}

	// {{{ tab: network

	GtkWidget *entry_net_ip_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_1"));
	GtkWidget *entry_net_ip_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_2"));
	GtkWidget *entry_net_ip_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_3"));
	GtkWidget *entry_net_ip_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_ip_4"));


	GtkWidget *entry_net_mask_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_1"));
	GtkWidget *entry_net_mask_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_2"));
	GtkWidget *entry_net_mask_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_3"));
	GtkWidget *entry_net_mask_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mask_4"));


	GtkWidget *entry_net_gw_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_1"));
	GtkWidget *entry_net_gw_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_2"));
	GtkWidget *entry_net_gw_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_3"));
	GtkWidget *entry_net_gw_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_gw_4"));


	GtkWidget *entry_net_mac_1 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_1"));
	GtkWidget *entry_net_mac_2 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_2"));
	GtkWidget *entry_net_mac_3 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_3"));
	GtkWidget *entry_net_mac_4 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_4"));
	GtkWidget *entry_net_mac_5 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_5"));
	GtkWidget *entry_net_mac_6 = GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_net_mac_6"));


	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_1), cfg.tab_network.mac[0]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_2), cfg.tab_network.mac[1]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_3), cfg.tab_network.mac[2]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_4), cfg.tab_network.mac[3]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_5), cfg.tab_network.mac[4]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mac_6), cfg.tab_network.mac[5]);

	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_1), cfg.tab_network.ip[0]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_2), cfg.tab_network.ip[1]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_3), cfg.tab_network.ip[2]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_ip_4), cfg.tab_network.ip[3]);

	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_1), cfg.tab_network.mask[0]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_2), cfg.tab_network.mask[1]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_3), cfg.tab_network.mask[2]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_mask_4), cfg.tab_network.mask[3]);

	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_1), cfg.tab_network.gw[0]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_2), cfg.tab_network.gw[1]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_3), cfg.tab_network.gw[2]);
	gtk_entry_set_text(GTK_ENTRY(entry_net_gw_4), cfg.tab_network.gw[3]);
	// }}}

}
// }}}

// {{{ port_update()
void port_update()
{
	int i;

	// {{{ tab: Puertos Virtuales
	for(i=0; i<16; i++)
	{
		// group
		if(strcmp(cfg.tab_virtual[i].type, "Operación")==0)
		{
			virtual_port_group_vp1_select(vsw[i], cfg.tab_virtual[i].port1);
			virtual_port_group_vp2_select(vsw[i], cfg.tab_virtual[i].port2);
		}
		
		// trigger
		if(strcmp(cfg.tab_virtual[i].type, "Disparador")==0)
		{

			virtual_port_trigger_cndp_select(vsw[i], cfg.tab_virtual[i].cndp);
			virtual_port_trigger_outp_select(vsw[i], cfg.tab_virtual[i].outp);
		}

	}
	// }}}

	// {{{ tab: Enlaces
	for(i=0; i<16; i++)
	{
		if(i<8)
		{
			link_sport_select(lsw1[i], cfg.tab_links[i].src);
			link_dport_select(lsw1[i], cfg.tab_links[i].dst);
		}
		else
		{		
			link_sport_select(lsw2[i-8], cfg.tab_links[i].src);
			link_dport_select(lsw2[i-8], cfg.tab_links[i].dst);
		}
	}

	// }}}


}
// }}}

// {{{ cb_save()
G_MODULE_EXPORT void cb_save( GtkButton *button, Data *data )
{
	char cmd[128];
	char buffer[64];
	int i;	

	const char connerr[] = 
	"No se ha podido conectar con ODControl, revise la dirección de conexión.";

	const char *host = gtk_entry_get_text(GTK_ENTRY(GTK_WIDGET(
		gtk_builder_get_object(builder, "entry_odcontrol_addr"))));

	// {{{ tab odcontrol
	const char *board_name = get_entry_value("entry_bname");
	const char *web_user = get_entry_value("entry_webuser");
	const char *web_pass = get_entry_value("entry_webpass");


	if(strcmp(board_name, cfg.tab_odcontrol.board_name)!=0)
	{
		if(!is_text(board_name) || strlen(board_name)!=5)
			return_message("Nombre de placa con formato incorrecto.");

		snprintf(cmd, sizeof(cmd), "snm %s\n", board_name);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
			return_message(connerr);

		strncpy(cfg.tab_odcontrol.board_name, board_name, CONFIG_STRING);
	}

	if(strcmp(web_user, cfg.tab_odcontrol.web_user)!=0)
	{
		if(!is_text(web_user))
			return_message("Usuario web con formato incorrecto.");

		if(!is_text(web_pass))
			return_message("Password web con formato incorrecto.");

		snprintf(cmd, sizeof(cmd), "sec webui %s:%s\n", web_user, web_pass);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
			return_message(connerr);
		
		strncpy(cfg.tab_odcontrol.web_user, web_user, CONFIG_STRING);
		strncpy(cfg.tab_odcontrol.web_pass, web_pass, CONFIG_STRING);
	}

	// }}}

	// {{{ tab analogic
	for(i=0; i<8; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name),"checkbutton_ai00%d_enabled",i);
		int active = get_checkbutton_value(widget_name);

		snprintf(widget_name, sizeof(widget_name), "checkbutton_ai00%d", i);
		int visible = get_checkbutton_value(widget_name);

		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d_A", i);
		const char *A_param = get_entry_value(widget_name);

		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d_B", i);
		const char *B_param = get_entry_value(widget_name);

		snprintf(widget_name, sizeof(widget_name), "adjustment_ai00%d", i);
		int tolerance = get_adjustment_value(widget_name);


		if(active != cfg.tab_analogic[i].active)
		{
			if(active)
				snprintf(cmd, sizeof(cmd), "cfg %s ai\n", cfg.tab_analogic[i].port);
			else
				snprintf(cmd, sizeof(cmd), "cfg %s x\n", cfg.tab_analogic[i].port);

			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);
		
			cfg.tab_analogic[i].active = active;
		}

		if(visible != cfg.tab_analogic[i].visible)
		{
			if(visible)
				snprintf(cmd, sizeof(cmd), "cfg %s s\n", cfg.tab_analogic[i].port);
			else
				snprintf(cmd, sizeof(cmd), "cfg %s h\n", cfg.tab_analogic[i].port);

			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);
		
			cfg.tab_analogic[i].visible = visible;
		}

		if(strcmp(A_param, cfg.tab_analogic[i].A_param)!=0)
		{
			if(!is_num(A_param))
				return_message("Parámetro A con formato incorrecto.");

			snprintf(cmd, sizeof(cmd), "fxa %s %s\n", 
				cfg.tab_analogic[i].port, A_param); 
			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);

			strncpy(cfg.tab_analogic[i].A_param, A_param, CONFIG_STRING);
		}

		if(strcmp(B_param, cfg.tab_analogic[i].B_param)!=0)
		{
			if(!is_num(B_param))
				return_message("Parámetro B con formato incorrecto.");

			snprintf(cmd, sizeof(cmd), "fxb %s %s\n", 
				cfg.tab_analogic[i].port, B_param); 
			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);

			strncpy(cfg.tab_analogic[i].B_param, B_param, CONFIG_STRING);
		}


		if(tolerance != cfg.tab_analogic[i].tolerance)
		{
			snprintf(cmd, sizeof(cmd), "tol %s %d\n", 
				cfg.tab_analogic[i].port, tolerance);

			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);
		
			cfg.tab_analogic[i].tolerance = tolerance;
		}


	}
	// }}}

	// {{{ tab digital input
	for(i=0; i<8; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name), "checkbutton_di00%d", i);
		int visible = get_checkbutton_value(widget_name);

		if(visible != cfg.tab_digital_i[i].visible)
		{
			if(visible)
				snprintf(cmd, sizeof(cmd), "cfg %s s\n", cfg.tab_digital_i[i].port);
			else
				snprintf(cmd, sizeof(cmd), "cfg %s h\n", cfg.tab_digital_i[i].port);

			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);
		
			cfg.tab_digital_i[i].visible = visible;
		}
	}
	// }}}

	// {{{ tab digital output
	for(i=0; i<8; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name), "checkbutton_do00%d", i);
		int visible = get_checkbutton_value(widget_name);

		if(visible != cfg.tab_digital_o[i].visible)
		{
			if(visible)
				snprintf(cmd, sizeof(cmd), "cfg %s s\n", cfg.tab_digital_o[i].port);
			else
				snprintf(cmd, sizeof(cmd), "cfg %s h\n", cfg.tab_digital_o[i].port);

			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);
		
			cfg.tab_digital_o[i].visible = visible;
		}
	}
	// }}}

	// {{{ tab virtual
	for(i=0; i<16; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name), "entry_vt0%.2d", i);
		const char *pname = get_entry_value(widget_name);

		snprintf(widget_name, sizeof(widget_name), "checkbutton_vt0%.2d", i);
		int visible = get_checkbutton_value(widget_name);

		if(strlen(pname)==0)
				return_message("Puerto virtual sin nombre asignado.");


		if(visible != cfg.tab_virtual[i].visible)
		{
			if(visible)
				snprintf(cmd, sizeof(cmd), "cfg %s s\n", cfg.tab_virtual[i].port);
			else
				snprintf(cmd, sizeof(cmd), "cfg %s h\n", cfg.tab_virtual[i].port);

			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);
		
			cfg.tab_virtual[i].visible = visible;
		}

		if(strcmp(vsw[i]->type_selected, "Valor")==0)
		{
			const char *value = gtk_entry_get_text(
				GTK_ENTRY(vsw[i]->entry_v_value));

			if(strcmp(vsw[i]->value_ad_selected, cfg.tab_virtual[i].ad)!=0)
			{
				if(strcmp(vsw[i]->value_ad_selected, "Digital")==0)
				{
					snprintf(cmd, sizeof(cmd), "val %s\n", cfg.tab_virtual[i].port);
					if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
						return_message(connerr);

					snprintf(cmd, sizeof(cmd), "cfg %s d\n",
						cfg.tab_virtual[i].port);
					if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
						return_message(connerr);

					strncpy(cfg.tab_virtual[i].ad, "Digital", CONFIG_STRING);
					cfg.tab_virtual[i].value[0]=0;
				}
				else		
				{
					snprintf(cmd, sizeof(cmd), "val %s\n", cfg.tab_virtual[i].port);
					if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
						return_message(connerr);

					snprintf(cmd, sizeof(cmd), "cfg %s a\n", 
						cfg.tab_virtual[i].port);
					if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
						return_message(connerr);

					strncpy(cfg.tab_virtual[i].ad, "Analógico", CONFIG_STRING);
					cfg.tab_virtual[i].value[0]=0;
				}
			}



			if(strcmp(value, cfg.tab_virtual[i].value)!=0)
			{
				if(strcmp(vsw[i]->value_ad_selected, "Digital")==0)
				{
					if(strcasecmp(value, "on")!=0 &&
						strcasecmp(value, "off")!=0)
						return_message("Puerto virtual de tipo valor digital, "
							" con formato incorrecto. Use los valores ON y OFF.");

					snprintf(cmd, sizeof(cmd), "set %s %s\n", 
						cfg.tab_virtual[i].port, value);
					if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
						return_message(connerr);

				}
				else
				{
					if(!is_real(value))
						return_message("Puerto virtual de tipo valor analógico, "
						" con formato incorrecto. Use un valor te tipo +0000.0000.");

	           	setlocale(LC_ALL, "C");
					int64_t fval=(atof(value)*10000);

		         if(fval > 99999999)
						return_message("Puerto virtual de tipo valor analógico, "
						" con formato incorrecto. Valor demasiado grande.");

					if(fval < -99999999)      
						return_message("Puerto virtual de tipo valor analógico, "
						" con formato incorrecto. Valor demasiado pequeño.");
	
					snprintf(cmd, sizeof(cmd), 
						"set %s %d\n", cfg.tab_virtual[i].port, (int)fval);
					if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
						return_message(connerr);
				}

				strncpy(cfg.tab_virtual[i].value, value, CONFIG_STRING);
				strcpy(cfg.tab_virtual[i].type, "Valor");
			}
		}
		else if(strcmp(vsw[i]->type_selected, "Operación")==0)
		{

			if(strcmp(vsw[i]->group_vp1_selected, cfg.tab_virtual[i].port1)!=0 ||
				strcmp(vsw[i]->group_vp2_selected, cfg.tab_virtual[i].port2)!=0 ||
				strcmp(vsw[i]->group_op_selected, cfg.tab_virtual[i].op)!=0 )
			{
				printf("%s=%s, %s=%s, %s=%s\n", 
					vsw[i]->group_vp1_selected, cfg.tab_virtual[i].port1, 
					vsw[i]->group_vp2_selected, cfg.tab_virtual[i].port2,
					vsw[i]->group_op_selected, cfg.tab_virtual[i].op);

				char *op = (char*)"add";
				if(vsw[i]->group_op_selected[0] == '+') op=(char*)"add";
				else if(vsw[i]->group_op_selected[0] == '-') op=(char*)"sub";
				else if(vsw[i]->group_op_selected[0] == '|') op=(char*)"or";
				else if(vsw[i]->group_op_selected[0] == '&') op=(char*)"and";

				snprintf(cmd, sizeof(cmd), "grp %s %s %s %s\n", 
					cfg.tab_virtual[i].port, 
					vsw[i]->group_vp1_selected, vsw[i]->group_vp2_selected, op);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				strncpy(cfg.tab_virtual[i].port1, 
					vsw[i]->group_vp1_selected, CONFIG_STRING);
				strncpy(cfg.tab_virtual[i].port2, 
					vsw[i]->group_vp2_selected, CONFIG_STRING);
				strncpy(cfg.tab_virtual[i].op, 
					vsw[i]->group_op_selected, CONFIG_STRING);

				strcpy(cfg.tab_virtual[i].type, "Operación");
			}


		}
		else if(strcmp(vsw[i]->type_selected, "Disparador")==0)
		{
			if(strcmp(vsw[i]->trigger_cnd_selected, cfg.tab_virtual[i].cnd)!=0 ||
			strcmp(vsw[i]->trigger_cndp_selected, cfg.tab_virtual[i].cndp)!=0 ||
			strcmp(vsw[i]->trigger_outval_selected,cfg.tab_virtual[i].outval)!=0||
			strcmp(vsw[i]->trigger_outp_selected, cfg.tab_virtual[i].outp)!=0 )
			{
				char *cnd = (char*)"cmpeq";
				if(strcmp(vsw[i]->trigger_cnd_selected,"==")==0)
					cnd=(char*)"cmpeq";
				else if(strcmp(vsw[i]->trigger_cnd_selected,"!=")==0)
					cnd=(char*)"cmpne";
				else if(strcmp(vsw[i]->trigger_cnd_selected,"<=")==0)
					cnd=(char*)"cmple";
				else if(strcmp(vsw[i]->trigger_cnd_selected,">=")==0)
					cnd=(char*)"cmpge";
				else if(strcmp(vsw[i]->trigger_cnd_selected," >")==0)
					cnd=(char*)"cmpgt";
				else if(strcmp(vsw[i]->trigger_cnd_selected," <")==0)
					cnd=(char*)"cmplt";

				snprintf(cmd, sizeof(cmd), "trg %s %s %s %s %s\n", 
					cfg.tab_virtual[i].port, cnd, 
					vsw[i]->trigger_cndp_selected, vsw[i]->trigger_outp_selected, 
					vsw[i]->trigger_outval_selected);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				strncpy(cfg.tab_virtual[i].cnd, 
					vsw[i]->trigger_cnd_selected, CONFIG_STRING);
				strncpy(cfg.tab_virtual[i].cndp, 
					vsw[i]->trigger_cndp_selected, CONFIG_STRING);
				strncpy(cfg.tab_virtual[i].outp, 
					vsw[i]->trigger_outp_selected, CONFIG_STRING);

				strncpy(cfg.tab_virtual[i].outval, 
					vsw[i]->trigger_outval_selected, CONFIG_STRING);

				strcpy(cfg.tab_virtual[i].type, "Disparador");
			}
		}
		else if(strcmp(vsw[i]->type_selected, "--")==0)
		{
			if(strcmp(vsw[i]->type_selected, cfg.tab_virtual[i].type)!=0)
			{
				snprintf(cmd, sizeof(cmd), "clv %s\n", cfg.tab_virtual[i].port);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);
				
				strcpy(cfg.tab_virtual[i].type, "--");
			}
	
		}
	}
	// }}}

	// {{{ tab links
	for(i=0; i<16; i++)
	{
		if(i<8)
		{

			if(strcmp(lsw1[i]->type_selected, "--")==0 &&
			   strcmp(lsw1[i]->type_selected, cfg.tab_links[i].type)==0)
				continue;

			if(strcmp(lsw1[i]->type_selected, "--")==0 &&
			   strcmp(lsw1[i]->type_selected, cfg.tab_links[i].type)!=0)
			{
				snprintf(cmd, sizeof(cmd), "unl %s %s\n",
						lsw1[i]->sp_selected, lsw1[i]->dp_selected);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				strncpy(cfg.tab_links[i].src,lsw1[i]->sp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].dst,lsw1[i]->dp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].type, "--", CONFIG_STRING);
				continue;
			}

			if(strcmp(lsw1[i]->type_selected, cfg.tab_links[i].type)!=0 ||
				strcmp(lsw1[i]->sp_selected, cfg.tab_links[i].src)!=0 ||
				strcmp(lsw1[i]->dp_selected, cfg.tab_links[i].dst)!=0 )
			{
				char *type = (char*)"d";
				if(strcmp(lsw1[i]->type_selected, "Directo")==0) type=(char*)"d";
				else if(strcmp(lsw1[i]->type_selected,"Inverso")==0)type=(char*)"i";
				else if(strcmp(lsw1[i]->type_selected, "Pulso")==0)type=(char*)"p";
				else if(strcmp(lsw1[i]->type_selected, "Conmutado")==0)
					type=(char*)"c";

				snprintf(cmd, sizeof(cmd), "unl %s %s\n",
						lsw1[i]->sp_selected, lsw1[i]->dp_selected);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				snprintf(cmd, sizeof(cmd), "lnk %s %s %s\n",
						lsw1[i]->sp_selected, lsw1[i]->dp_selected, type);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				strncpy(cfg.tab_links[i].src,lsw1[i]->sp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].dst,lsw1[i]->dp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].type,lsw1[i]->type_selected,CONFIG_STRING);
			}
		}
		else
		{
			if(strcmp(lsw2[i-8]->type_selected, "--")==0 &&
			   strcmp(lsw2[i-8]->type_selected, cfg.tab_links[i].type)==0)
				continue;

			if(strcmp(lsw2[i-8]->type_selected, "--")==0 &&
			   strcmp(lsw2[i-8]->type_selected, cfg.tab_links[i].type)!=0)
			{
				snprintf(cmd, sizeof(cmd), "unl %s %s\n",
						lsw2[i-8]->sp_selected, lsw2[i-8]->dp_selected);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				strncpy(cfg.tab_links[i].src,lsw2[i-8]->sp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].dst,lsw2[i-8]->dp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].type, "--", CONFIG_STRING);
				continue;
			}

			if(strcmp(lsw2[i-8]->type_selected, cfg.tab_links[i].type)!=0 ||
				strcmp(lsw2[i-8]->sp_selected, cfg.tab_links[i].src)!=0 ||
				strcmp(lsw2[i-8]->dp_selected, cfg.tab_links[i].dst)!=0 )
			{
				char *type = (char*)"d";
				if(strcmp(lsw2[i-8]->type_selected, "Directo")==0) 
					type=(char*)"d";
				else if(strcmp(lsw2[i-8]->type_selected,"Inverso")==0)
					type=(char*)"i";
				else if(strcmp(lsw2[i-8]->type_selected,"Pulso")==0)
					type=(char*)"p";
				else if(strcmp(lsw2[i-8]->type_selected,"Conmutado")==0)
					type=(char*)"c";

				snprintf(cmd, sizeof(cmd), "unl %s %s\n",
						lsw2[i-8]->sp_selected, lsw2[i-8]->dp_selected);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				snprintf(cmd, sizeof(cmd), "lnk %s %s %s\n",
						lsw2[i-8]->sp_selected, lsw2[i-8]->dp_selected, type);
				if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
					return_message(connerr);

				strncpy(cfg.tab_links[i].src,lsw2[i-8]->sp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].dst,lsw2[i-8]->dp_selected,CONFIG_STRING);
				strncpy(cfg.tab_links[i].type,
						lsw2[i-8]->type_selected,CONFIG_STRING);
			}
		}
	}
	// }}}

	// {{{ tab network
	const char *mac1 = get_entry_value("entry_net_mac_1");
	const char *mac2 = get_entry_value("entry_net_mac_2");
	const char *mac3 = get_entry_value("entry_net_mac_3");
	const char *mac4 = get_entry_value("entry_net_mac_4");
	const char *mac5 = get_entry_value("entry_net_mac_5");
	const char *mac6 = get_entry_value("entry_net_mac_6");

	if(strcmp(mac1, cfg.tab_network.mac[0])!=0 ||
	   strcmp(mac2, cfg.tab_network.mac[1])!=0 ||
	   strcmp(mac3, cfg.tab_network.mac[2])!=0 ||
	   strcmp(mac4, cfg.tab_network.mac[3])!=0 ||
	   strcmp(mac5, cfg.tab_network.mac[4])!=0 ||
	   strcmp(mac6, cfg.tab_network.mac[5])!=0)
	{
		if(!is_hex(mac1) ||
		   !is_hex(mac2) ||
		   !is_hex(mac3) ||
		   !is_hex(mac4) ||
		   !is_hex(mac5) ||
		   !is_hex(mac6) )
			return_message("La dirección MAC tiene formato incorrecto.");

		snprintf(cmd, sizeof(cmd), "net mc %s:%s:%s:%s:%s:%s\n",
			mac1, mac2, mac3, mac4, mac5, mac6);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
			return_message(connerr);

		strncpy(cfg.tab_network.mac[0], mac1, CONFIG_STRING);
		strncpy(cfg.tab_network.mac[1], mac2, CONFIG_STRING);
		strncpy(cfg.tab_network.mac[2], mac3, CONFIG_STRING);
		strncpy(cfg.tab_network.mac[3], mac4, CONFIG_STRING);
		strncpy(cfg.tab_network.mac[4], mac5, CONFIG_STRING);
		strncpy(cfg.tab_network.mac[5], mac6, CONFIG_STRING);
	}

	const char *ip1 = get_entry_value("entry_net_ip_1");
	const char *ip2 = get_entry_value("entry_net_ip_2");
	const char *ip3 = get_entry_value("entry_net_ip_3");
	const char *ip4 = get_entry_value("entry_net_ip_4");

	if(strcmp(ip1, cfg.tab_network.ip[0])!=0 ||
	   strcmp(ip2, cfg.tab_network.ip[1])!=0 ||
	   strcmp(ip3, cfg.tab_network.ip[2])!=0 ||
	   strcmp(ip4, cfg.tab_network.ip[3])!=0)
	{
		if(!is_num(ip1) ||
		   !is_num(ip2) ||
		   !is_num(ip3) ||
		   !is_num(ip4) )
			return_message("La dirección IP tiene formato incorrecto.");

		snprintf(cmd, sizeof(cmd), "net ip %s.%s.%s.%s\n",
			ip1, ip2, ip3, ip4);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
			return_message(connerr);

		strncpy(cfg.tab_network.ip[0], ip1, CONFIG_STRING);
		strncpy(cfg.tab_network.ip[1], ip2, CONFIG_STRING);
		strncpy(cfg.tab_network.ip[2], ip3, CONFIG_STRING);
		strncpy(cfg.tab_network.ip[3], ip4, CONFIG_STRING);
	}

	const char *mask1 = get_entry_value("entry_net_mask_1");
	const char *mask2 = get_entry_value("entry_net_mask_2");
	const char *mask3 = get_entry_value("entry_net_mask_3");
	const char *mask4 = get_entry_value("entry_net_mask_4");

	if(strcmp(mask1, cfg.tab_network.mask[0])!=0 ||
	   strcmp(mask2, cfg.tab_network.mask[1])!=0 ||
	   strcmp(mask3, cfg.tab_network.mask[2])!=0 ||
	   strcmp(mask4, cfg.tab_network.mask[3])!=0)
	{
		if(!is_num(mask1) ||
		   !is_num(mask2) ||
		   !is_num(mask3) ||
		   !is_num(mask4) )
			return_message("La máscara de red tiene formato incorrecto.");

		snprintf(cmd, sizeof(cmd), "net mask %s.%s.%s.%s\n",
			mask1, mask2, mask3, mask4);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
			return_message(connerr);

		strncpy(cfg.tab_network.mask[0], mask1, CONFIG_STRING);
		strncpy(cfg.tab_network.mask[1], mask2, CONFIG_STRING);
		strncpy(cfg.tab_network.mask[2], mask3, CONFIG_STRING);
		strncpy(cfg.tab_network.mask[3], mask4, CONFIG_STRING);
	}
	
	const char *gw1 = get_entry_value("entry_net_gw_1");
	const char *gw2 = get_entry_value("entry_net_gw_2");
	const char *gw3 = get_entry_value("entry_net_gw_3");
	const char *gw4 = get_entry_value("entry_net_gw_4");

	if(strcmp(gw1, cfg.tab_network.gw[0])!=0 ||
	   strcmp(gw2, cfg.tab_network.gw[1])!=0 ||
	   strcmp(gw3, cfg.tab_network.gw[2])!=0 ||
	   strcmp(gw4, cfg.tab_network.gw[3])!=0)
	{
		if(!is_num(gw1) ||
		   !is_num(gw2) ||
		   !is_num(gw3) ||
		   !is_num(gw4) )
			return_message("La dirección del gateway tiene formato incorrecto.");

		snprintf(cmd, sizeof(cmd), "net gw %s.%s.%s.%s\n",
			gw1, gw2, gw3, gw4);
		if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
			return_message(connerr);

		strncpy(cfg.tab_network.gw[0], gw1, CONFIG_STRING);
		strncpy(cfg.tab_network.gw[1], gw2, CONFIG_STRING);
		strncpy(cfg.tab_network.gw[2], gw3, CONFIG_STRING);
		strncpy(cfg.tab_network.gw[3], gw4, CONFIG_STRING);
	}
	
	// }}}

	// -- lbl commands last --


	// {{{ tab analogic lbl
	for(i=0; i<8; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name), "entry_ai00%d", i);
		const char *pname = get_entry_value(widget_name);

		if(strcmp(pname, cfg.tab_analogic[i].port)!=0)
		{
			if(!is_text(pname) || strlen(pname)!=5)
				return_message("Puerto analógico con formato incorrecto.");

			snprintf(cmd, sizeof(cmd), "lbl %s %s\n", 
				cfg.tab_analogic[i].port, pname);
			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);

			strncpy(p_ai[i], pname, sizeof(p_ai[i]));
			port_update();
			strncpy(cfg.tab_analogic[i].port, pname, CONFIG_STRING);
		}
	}
	// }}}

	// {{{ tab digital input lbl
	for(i=0; i<8; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name), "entry_di00%d", i);
		const char *pname = get_entry_value(widget_name);

		if(strcmp(pname, cfg.tab_digital_i[i].port)!=0)
		{
			if(!is_text(pname) || strlen(pname)!=5)
				return_message("Puerto digital de entrada con formato incorrecto.");

			snprintf(cmd, sizeof(cmd), "lbl %s %s\n", 
				cfg.tab_digital_i[i].port, pname);
			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);

			strncpy(p_di[i], pname, sizeof(p_di[i]));
			port_update();
			strncpy(cfg.tab_digital_i[i].port, pname, CONFIG_STRING);
		}
	}
	// }}}

	// {{{ tab digital output lbl
	for(i=0; i<8; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name), "entry_do00%d", i);
		const char *pname = get_entry_value(widget_name);

		if(strcmp(pname, cfg.tab_digital_o[i].port)!=0)
		{
			if(!is_text(pname) || strlen(pname)!=5)
				return_message("Puerto digital de entrada con formato incorrecto.");

			snprintf(cmd, sizeof(cmd), "lbl %s %s\n", 
				cfg.tab_digital_o[i].port, pname);
			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);

			strncpy(p_do[i], pname, sizeof(p_do[i]));
			port_update();
			strncpy(cfg.tab_digital_o[i].port, pname, CONFIG_STRING);
		}
	}
	// }}}

	// {{{ tab virtual lbl
	for(i=0; i<16; i++)
	{
		char widget_name[32];

		snprintf(widget_name, sizeof(widget_name), "entry_vt0%.2d", i);
		const char *pname = get_entry_value(widget_name);

		if(strcmp(pname, cfg.tab_virtual[i].port)!=0)
		{
			if(!is_text(pname) || strlen(pname)!=5)
				return_message("Puerto digital de entrada con formato incorrecto.");

			snprintf(cmd, sizeof(cmd), "lbl %s %s\n", 
				cfg.tab_virtual[i].port, pname);
			if(exec_cmd(cmd, host, buffer, sizeof(buffer))<0)
				return_message(connerr);

			strncpy(p_vt[i], pname, sizeof(p_vt[i]));
			port_update();
			strncpy(cfg.tab_virtual[i].port, pname, CONFIG_STRING);
		}
	
	}
	// }}}

}
// }}}

int main(int argc, char **argv)
{
   GtkWidget  *window;
	Data data;

	gtk_init(&argc, &argv);
	
	builder = gtk_builder_new();
	gtk_builder_add_from_file( builder, "ui.glade", NULL );

	data.builder = builder;
	window = GTK_WIDGET( gtk_builder_get_object( builder, "window_main" ) );
	gtk_builder_connect_signals(builder, &data);

	// {{{ Dynamic widgets
	int i;
	for(i=0; i<8; i++)
	{	
		snprintf(p_ai[i], sizeof(p_ai[i]), "ai00%d", i);
		snprintf(p_di[i], sizeof(p_di[i]), "di00%d", i);
		snprintf(p_do[i], sizeof(p_do[i]), "do00%d", i);
	}
	for(i=0; i<16; i++)
		snprintf(p_vt[i], sizeof(p_vt[i]), "vt0%.2d", i);

	GtkWidget *fixed_virtual_ports = 
		GTK_WIDGET(gtk_builder_get_object(builder, "fixed_virtual_ports"));

	GtkWidget *fixed_links = 
		GTK_WIDGET(gtk_builder_get_object(builder, "fixed_links"));

	int x=0, y=40;
	for(i=0; i<16; i++)
	{
		vsw[i] = malloc(sizeof(virtual_selector_widgets_t));
		virtual_port_widgets_create(vsw[i], fixed_virtual_ports, x+105, y);
		strncpy(vsw[i]->type_selected, "--", sizeof(vsw[i]->type_selected));
		y+=30;
	}

	x=0, y=70;
	for(i=0; i<8; i++)
	{
		lsw1[i] = malloc(sizeof(link_selector_widgets_t));
		lsw2[i] = malloc(sizeof(link_selector_widgets_t));
		link_widgets_create(lsw1[i], fixed_links, x+30, y);
		link_widgets_create(lsw2[i], fixed_links, x+340, y);
		y+=50;
	}
	// }}}

	gtk_widget_show( window );
	gtk_main();

	g_object_unref( G_OBJECT( builder ) );

	return 0;
}

